package com.lancoo.ccas53.arrangecourse.dataprocess;

import cn.hutool.core.util.StrUtil;
import com.lancoo.ccas53.arrangecourse.entities.SplitCourse;
import com.lancoo.ccas53.arrangecourse.entities.TeachingClassUnit;
import com.lancoo.ccas53.exception.BizException;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description 奇偶教学班配对分组
 * @Author Fortysun
 * @Date 2023/9/15 15:20
 **/
@Slf4j
public class GenerateSplitClassGroup {
    /**
     * 将教学班排课单元进行配对和分组
     * @param teachingClassUnitList 拆班的教学班排课单元列表
     * @return
     */
    public LinkedList<TeachingClassUnit> start(LinkedList<TeachingClassUnit> teachingClassUnitList) {
        if (teachingClassUnitList != null && !teachingClassUnitList.isEmpty()) {
            //将教学班排课单元，按照分班标识，进行分组，生成map，key未分班标识(UUID字符串)
            //其实就是将每个行政班生成的拆班教学班，按照分班标识，进行了分组
            //每个行政班的数据都在一起
            Map<String, LinkedList<TeachingClassUnit>> classUnitMap = teachingClassUnitList.stream()
                    .collect(Collectors.groupingBy(TeachingClassUnit::getUniqueShiftSign, Collectors.toCollection(LinkedList::new)));
            //定义用来保存已经标记分组的教学班排课单元列表
            LinkedList<TeachingClassUnit> groupedList=new LinkedList<>();

            //按照行政班，一个个去遍历待排教学班排课单元map
            for (Map.Entry<String, LinkedList<TeachingClassUnit>> entry : classUnitMap.entrySet()) {
                //处理一个行政班的奇偶教学班的配对，返回配对后的教学班排课单元列表信息
                LinkedList<TeachingClassUnit> retList = generateClassUnitGroup(entry.getValue());
                //将配对的结果，加入分组后的列表
                groupedList.addAll(retList);
            }

            return groupedList;
        }
        log.error("#GenerateSplitClassGroup.getclassUnitGroupMap# 传入的教学班排课单元列表为空，无需进行分组，请排查原因~");
        return null;
    }

    /**
     * 生成指定行政班，拆出来的教学班的分组信息
     * @param phyClassUnitList 某个行政班对应的，拆班教学班列表
     * @return
     */
    private LinkedList<TeachingClassUnit> generateClassUnitGroup(LinkedList<TeachingClassUnit> phyClassUnitList) {
        if (phyClassUnitList == null || phyClassUnitList.isEmpty()) {
            log.error("#GenerateSplitClassGroup.generateClassUnitGroup# 传入的教学班排课单元列表为空，无需进行分组，请排查原因~");
            return null;
        }
        //定义用来保存已经标记分组的教学班排课单元列表
        LinkedList<TeachingClassUnit> groupedList=new LinkedList<>();
        //将拆班教学班列表，格式化保存到SplitCourse对象，方便进行配对
        LinkedList<SplitCourse> splitCourseList = getSplitCourseList(phyClassUnitList);
        if (splitCourseList != null && !splitCourseList.isEmpty()) {
            //定义配对处理的循环
            int i = phyClassUnitList.size() * phyClassUnitList.size();
            while (i >= 0) {
                //如果所有课程都配对完成，跳出循环
                if (splitCourseList.isEmpty()) {
                    break;
                }
                //获取列表中，教学班数量最大的那个元素
                //即奇数教学班排课列表的大小+偶数列表的大小
                Optional<SplitCourse> maxObject = splitCourseList.stream()
                        .max(Comparator.comparing(SplitCourse::getSize));

                if (maxObject.isPresent()) {

                    //从Optional中获取拆班课程对象
                    SplitCourse maxCourse = maxObject.get();

                    //从列表中删除最大的课程
                    splitCourseList.remove(maxCourse);
                    //定义列表的迭代器
                    Iterator iterator = splitCourseList.iterator();
                    //如果教学班数量最多的课程，偶数教学班数量，大于等于奇数教学班数量
                    //用最多的这个课程的偶数教学班，去匹配其他教学班的奇数教学班
                    if (maxCourse.getEvenClassList().size() >= maxCourse.getOddClassList().size()) {
                        //遍历课程列表，用最大课程的偶数教学班，去配对其他教学班里面的奇数教学班
                        while (iterator.hasNext()) {
                            //获取当前迭代的课程
                            SplitCourse splitCourse = (SplitCourse)iterator.next();
                            //如果拆班教学班，奇数教学班数量大于0，才进行分组
                            if (maxCourse.getEvenClassList().size() > 0
                                    && splitCourse.getOddClassList().size() > 0
//                                    && !maxCourse.getEvenClassList().getFirst().getTeacherIds().equals(splitCourse.getOddClassList().getFirst().getTeacherIds())
                                    && !maxCourse.getEvenClassList().getFirst().getRoomId().equals(splitCourse.getOddClassList().getFirst().getRoomId())
                            ) {
                                groupedList = startGroup(maxCourse, splitCourse, groupedList);
                                //如果这个课程配对完成后，没有需要配对的排课教学班单元了，
                                if (splitCourse.getSize().equals(0)) {
                                    //那么将其从拆班教学班列表中移除
                                    iterator.remove();
                                }
                            }
                        }

                    } else {//用最多的这个课程的奇数教学班，去匹配其他教学班的偶数教学班
                        //遍历课程列表，用最大课程的奇数教学班，去配对其他教学班里面的偶数教学班
                        while (iterator.hasNext()) {
                            //获取当前迭代的课程
                            SplitCourse splitCourse = (SplitCourse)iterator.next();
                            //如果拆班教学班，奇数教学班数量大于0，才进行分组
                            if (maxCourse.getOddClassList().size() > 0
                                    && splitCourse.getEvenClassList().size() > 0
//                                    && !maxCourse.getOddClassList().getFirst().getTeacherIds().equals(splitCourse.getEvenClassList().getFirst().getTeacherIds())
                                    && !maxCourse.getOddClassList().getFirst().getRoomId().equals(splitCourse.getEvenClassList().getFirst().getRoomId())
                            ) {
                                groupedList = startGroup(splitCourse, maxCourse, groupedList);
                                //如果这个课程配对完成后，没有需要配对的排课教学班单元了，
                                if (splitCourse.getSize().equals(0)) {
                                    //那么将其从拆班教学班列表中移除
                                    iterator.remove();
                                }
                            }
                        }
                    }
                    //如果最大的课程配对完，还剩余了拆班教学班
                    if (!maxCourse.getSize().equals(0)) {
                        //将最大的课程，加入列表，进行下一轮的比较
                        splitCourseList.add(maxCourse);
                    }
                }
                i--;
            }
        }
        if (groupedList.size() == 0 || groupedList.stream().anyMatch(o -> StrUtil.isEmpty(o.getSplitClassGroupId()))){
            throw new BizException("分组失败，分组数据不合理");
        }

        return groupedList;
    }

    /**
     * 开始进行分组配对
     * @param evenCourse 分配偶数教学班配对的课程
     * @param oddCourse 分配奇数教学班配对的课程
     */
    private LinkedList<TeachingClassUnit> startGroup(SplitCourse evenCourse,
                            SplitCourse oddCourse,
                            LinkedList<TeachingClassUnit> groupedList) {
        //生成一个UUID，作为分组id
        String splitClassGroupId = UUID.randomUUID().toString();
        //将最大课程的偶数教学班排课单元列表中的第一个元素取出来
        TeachingClassUnit evenUnit = evenCourse.getEvenClassList().getFirst();
        //设置配对的分组id
        evenUnit.setSplitClassGroupId(splitClassGroupId);
        //将配对的教学班排课单元对象加入返回列表
        groupedList.add(evenUnit);
        //将配对完成的教学班排课单元对象，从最大的课程的偶数教学班排课单元列表中移除
        evenCourse.getEvenClassList().remove(evenUnit);

        //将其他课程的奇数教学班排课单元列表中的第一个元素取出来
        TeachingClassUnit oddUnit = oddCourse.getOddClassList().getFirst();
        //设置配对的分组id
        oddUnit.setSplitClassGroupId(splitClassGroupId);
        //将配对的教学班排课单元对象加入返回列表
        groupedList.add(oddUnit);
        //将配对完成的教学班排课单元对象，从课程的奇数数教学班排课单元列表中移除
        oddCourse.getOddClassList().remove(oddUnit);

        return groupedList;
    }

    /**
     * 格式化保存数据，将拆班课程教学班列表，格式化保存到拆班课程对象
     * @param phyClassUnitList 指定行政班，拆班的教学班列表
     * @return
     */
    private LinkedList<SplitCourse> getSplitCourseList(LinkedList<TeachingClassUnit> phyClassUnitList) {
        if (phyClassUnitList == null || phyClassUnitList.isEmpty()) {
            log.error("#GenerateSplitClassGroup.getSplitCourseList# 传入的教学班排课单元列表为空，无需进行分组，请排查原因~");
            return null;
        }
        //将指定行政班的拆班教学班，按照课程进行分组
        Map<String, LinkedList<TeachingClassUnit>> phyUnitMap = phyClassUnitList.stream()
                .collect(Collectors.groupingBy((TeachingClassUnit::getCourseTeacherIDs), Collectors.toCollection(LinkedList::new)));
        //定义返回列表
        LinkedList<SplitCourse> retList = new LinkedList<>();
        if (phyUnitMap != null && !phyUnitMap.isEmpty()) {
            //遍历map，将每个课程拆出来的行政班，按照单班和双班的形式，存入SplitCourse
            for (Map.Entry<String, LinkedList<TeachingClassUnit>> entry : phyUnitMap.entrySet()) {
                //定义存储奇数教学班的列表
                LinkedList<TeachingClassUnit> oddList = new LinkedList<>();
                //定义存储偶数教学班的列表
                LinkedList<TeachingClassUnit> evenList = new LinkedList<>();
                for (TeachingClassUnit teachingClassUnit : entry.getValue()) {
                    //奇数
                    if (teachingClassUnit.getIsSingle().equals(1)) {
                        oddList.add(teachingClassUnit);
                    } else {//偶数
                        evenList.add(teachingClassUnit);
                    }
                }
                //生成分班课程对象
                SplitCourse splitCourse = new SplitCourse(entry.getKey(), oddList, evenList);
                //将分班课程对象，加入返回值列表
                retList.add(splitCourse);
            }
        }
        return  retList;
    }
}
